﻿//#define GRAPH_DEBUG

#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
using Object = UnityEngine.Object;

namespace AI.Graph.Editor {


    public class AIGraph : EditorWindow {

        [Serializable] class SerializedController : SerializedObject<AIController> { }

        [Serializable] class SerializedAIBeahaviour : SerializedObject<AIBehaviour> { }


        public enum GraphMode {

            NoController,
            AssetController,
            AssetControllerAndAiBehaviour,
            RuntimeController,
            RuntimeControllerAndAiBehaviour

        }


#region Static

        /// <summary>
        /// The current AIGraph instance
        /// </summary>
        private static AIGraph instance;

        /// <summary>
        /// The controller that was loaded before the graph closed.
        /// </summary>
        private static AIController controllerBeforeGraphClosed;

        /// <summary>
        /// The ai that was loaded before the graph closed.
        /// </summary>
        private static AIBehaviour aiBehaviourBeforeGraphClosed;

        /// <summary>
        /// Dialog title on Unity close if certain controller have been modified.
        /// </summary>
        private const string dialogTitle = "AIController Have Been Modified";

        /// <summary>
        /// Dialog message on Unity close if certain controller have been modified.
        /// </summary>
        private const string dialogMessage = "You have modified AIController. If you close Unity all changes will be made persistent";

        /// <summary>
        /// Event if the controller changed.
        /// </summary>
        public static event Action<AIController, AIBehaviour> onControllerChanged;

        /// <summary>
        /// Event if the Graph Mode changed.
        /// Is always called if a controller was loaded.
        /// But only if the last graph mode is different.
        /// </summary>
        public static event Action<GraphMode> onGraphModeChanged;


        [MenuItem ("AI System/AI Graph")]
        public static void Open () {
            #if GRAPH_DEBUG
            if (instance == null) {
                Debug.Log ("[AI Graph]: Open Graph");
            }
            #endif

            instance = GetWindow<AIGraph> ();
            instance.titleContent = new GUIContent ("AI Graph");
            instance.minSize = new Vector2 (680, 390);
        }


        [MenuItem ("Window/AI Graph")]
        static void OpenUnityMenuItem () {
            Open ();
        }


        [OnOpenAsset]
        static bool DoubleClickOnController (int instanceID, int line) {
            Object obj = EditorUtility.InstanceIDToObject (instanceID);
            if (obj.IsTypeOf<AIController> ()) {
                #if GRAPH_DEBUG
                Debug.Log ("[AI Graph]: Double click on Controller");
                #endif

                Open ();
                LoadController ((AIController) obj, null, true);
                return true;
            }

            return false;
        }


        /// <summary>
        /// Load the given controller and draw all of it's nodes..
        /// </summary>
        public static void LoadController (AIController controller, AIBehaviour aiBehaviour = null, bool forceLoad = false) {
            if (aiBehaviour != null && aiBehaviour.sharedController != controller && aiBehaviour.runtimeController != controller) {
                Debug.Log ("[AI Graph]: Try Load controller with an ai behaviour with a different controller");
                return;
            }

            if (controller != null && !controller.isCopy) {
                controllerBeforeGraphClosed = controller;
                aiBehaviourBeforeGraphClosed = aiBehaviour;
            }
            else {
                controllerBeforeGraphClosed = null;
                aiBehaviourBeforeGraphClosed = null;
            }

            if (instance != null) {
                instance.LoadControllerInternal (controller, aiBehaviour, forceLoad);
                instance.Repaint ();
            }
        }

#endregion


#region Fields

        [SerializeField]
        private SerializedController m_controller = new SerializedController ();

        [SerializeField]
        private SerializedController m_controllerBeforeEditModeExited = new SerializedController ();

        [SerializeField]
        private SerializedAIBeahaviour m_aiBehaviour = new SerializedAIBeahaviour ();

        [SerializeField]
        private SerializedAIBeahaviour m_aiBehaviourBeforeEditModeExited = new SerializedAIBeahaviour ();

        private GraphMode m_graphMode;

        private NodeCanvas m_canvas;
        private EventSystem m_eventSystem;

        private Rect m_canvasRect;
        private Rect m_leftPanelRect;
        private Rect m_visualLeftPanelRect;
        private float m_leftPanelAnimationValue = 1;

        private int m_leftPanelSelectedToggleIndex;
        private int m_leftPanelSelectedToggleIndexBeforeEditModeExited;

        private ParameterList<ExposedParameter> m_exposedParameterList;
        private ParameterList<NodeParameter> m_nodeParameterList;
        private HistoryPanel m_historyPanel;
        private StatisticsPanel m_statsPanel;
        private LogPanel m_logPanel;

        private bool m_drawStats;
        private Rect m_statsToggleRect;

        private double m_deltaTime;
        private double m_lastOnGUICall;

#endregion


#region Properties

        /// <summary>
        /// The node canvas.
        /// </summary>
        public NodeCanvas canvas {
            get { return m_canvas; }
        }

        /// <summary>
        /// The EventSystem. Used for node events to select, move, etc.
        /// </summary>
        public EventSystem eventSystem {
            get { return m_eventSystem; }
        }


        /// <summary>
        /// The current controller
        /// </summary>
        public AIController controller {
            get { return m_controller.target; }
            private set { m_controller.target = value; }
        }

        /// <summary>
        /// The controller before playmode started.
        /// </summary>
        private AIController controllerBeforeEditModeExited {
            get { return m_controllerBeforeEditModeExited.target; }
            set { m_controllerBeforeEditModeExited.target = value; }
        }


        /// <summary>
        /// The connected aiBehaviour.
        /// </summary>
        public AIBehaviour aiBehaviour {
            get { return m_aiBehaviour.target; }
            private set { m_aiBehaviour.target = value; }
        }

        /// <summary>
        /// The AIBehaviour before play mode started.
        /// </summary>
        private AIBehaviour aiBehaviourBeforeEditModeExited {
            get { return m_aiBehaviourBeforeEditModeExited.target; }
            set { m_aiBehaviourBeforeEditModeExited.target = value; }
        }

        private bool isLeftPanelOpen {
            get { return m_leftPanelSelectedToggleIndex == 0 || m_leftPanelSelectedToggleIndex == 1 || m_leftPanelSelectedToggleIndex == 2 || m_leftPanelSelectedToggleIndex == 3; }
        }

        /// <summary>
        /// DeltaTime for editor animations. 
        /// </summary>
        public float deltaTime {
            get { return (float) m_deltaTime; }
        }

#endregion


#region Internal 

        void InitializeGraph () {
            // This Object should not be destroyed if the scene changed or if the editor switched the PlayMode.
            hideFlags = HideFlags.HideAndDontSave;

            // set the instance to the current open window.
            // instance is null if Unity starts and the window is open.
            instance = this;

            m_canvas = new NodeCanvas (this);
            m_eventSystem = new EventSystem (this);
            m_historyPanel = new HistoryPanel (this, Repaint);
            m_logPanel = new LogPanel (this, Repaint);
            m_statsPanel = new StatisticsPanel (this, Repaint);

            EditorApplication.wantsToQuit -= UnityWantsToQuit;
            EditorApplication.wantsToQuit += UnityWantsToQuit;

            EditorApplication.playModeStateChanged -= EditorApplicationOnPlayModeStateChanged;
            EditorApplication.playModeStateChanged += EditorApplicationOnPlayModeStateChanged;

            Selection.selectionChanged -= LoadSelectedController;
            Selection.selectionChanged += LoadSelectedController;

            SceneView.onSceneGUIDelegate -= OnSceneGUI;
            SceneView.onSceneGUIDelegate += OnSceneGUI;

            #if GRAPH_DEBUG
            Debug.Log ("[AI Graph]: Graph initialized");
            #endif
        }


        /// <summary>
        /// Load the given controller and create all necessary objects.
        /// </summary>
        void LoadControllerInternal (AIController newController, AIBehaviour newAIBehaviour = null, bool forceLoad = false) {
            if (!forceLoad) {
                if (controller == newController && aiBehaviour == newAIBehaviour) {
                    return;
                }
            }

            controller = newController;
            aiBehaviour = newAIBehaviour;

            if (newController != null) {
                m_exposedParameterList = new ParameterList<ExposedParameter> (GetExposedParameters, m_canvas, Repaint);
                m_exposedParameterList.onParameterAddClicked += CreateExposedParameter;
                m_exposedParameterList.onParameterRemovedClicked += RemoveExposedParameter;
                m_exposedParameterList.onChangeParameterIndex += ChangeExposedParameterIndex;
                m_exposedParameterList.onChangeParameterName += ChangeExposedParameterName;

                m_nodeParameterList = new ParameterList<NodeParameter> (GetNodeParameters, m_canvas, Repaint);
                m_nodeParameterList.onParameterAddClicked += CreateNodeParameter;
                m_nodeParameterList.onParameterRemovedClicked += RemoveNodeParameter;
                m_nodeParameterList.onChangeParameterIndex += ChangeNodeParameterIndex;
                m_nodeParameterList.onChangeParameterName += ChangeNodeParameterName;

                if (controller.isCopy) {
                    ShowNotification (new GUIContent ("AIController changes will not be saved"));
                }

                #if GRAPH_DEBUG
                Debug.Log ("[AI Graph]: Controller loaded (" + controller.name + ") AIBehaviour (" + aiBehaviour + ")");
                #endif
            }
            else {
                aiBehaviour = null;
                controller = null;
                m_exposedParameterList = null;
                m_nodeParameterList = null;

                #if GRAPH_DEBUG
                Debug.Log ("[AI Graph]: Controller loaded (null) AIBehaviour (null)");
                #endif
            }

            onControllerChanged?.Invoke (newController, newAIBehaviour);

            GraphMode oldGraphMode = m_graphMode;

            if (controller == null) {
                m_graphMode = GraphMode.NoController;
            }
            else if (controller.isCopy) {
                if (aiBehaviour == null) {
                    m_graphMode = GraphMode.RuntimeController;
                }
                else {
                    m_graphMode = GraphMode.RuntimeControllerAndAiBehaviour;
                }
            }
            else {
                if (aiBehaviour == null) {
                    m_graphMode = GraphMode.AssetController;
                }
                else {
                    m_graphMode = GraphMode.AssetControllerAndAiBehaviour;
                }
            }

            if (m_graphMode != oldGraphMode) {
                onGraphModeChanged?.Invoke (m_graphMode);
            }
        }


        /// <summary>
        /// Load the current selected Controller if one is selected.
        /// </summary>
        /// <returns></returns>
        bool TryLoadSelectedController () {
            if (SelectionUtility.IsObjectOfTypeSelected (out Node node)) {
                if (node.controller != controller) {
                    #if GRAPH_DEBUG
                    Debug.Log ("[AI Graph]: Change Controller during Node selection");
                    #endif
                    LoadController (node.controller);
                }

                return true;
            }

            if (SelectionUtility.IsObjectOfTypeSelected (out AIBehaviour aiBehaviour)) {
                if (aiBehaviour) {
                    if (aiBehaviour.sharedController == null) {
                        #if GRAPH_DEBUG
                        Debug.Log ("[AI Graph]: Change Controller during AIBehaviour selection (null)");
                        #endif

                        LoadController (null, null, true);
                    }
                    else {
                        if (EditorApplication.isPlaying) {
                            #if GRAPH_DEBUG
                            Debug.Log ("[AI Graph]: Change Controller during AIBehaviour selection (runtime)");
                            #endif

                            LoadController (aiBehaviour.runtimeController, aiBehaviour, true);
                        }
                        else {
                            #if GRAPH_DEBUG
                            Debug.Log ("[AI Graph]: Change Controller during AIBehaviour selection (runtime)");
                            #endif

                            LoadController (aiBehaviour.sharedController, aiBehaviour, true);
                        }
                    }

                    return true;
                }
            }

            if (SelectionUtility.IsObjectOfTypeSelected (out AIController selectedController)) {
                #if GRAPH_DEBUG
                Debug.Log ("[AI Graph]: Change Controller during Controller selection");
                #endif

                LoadController (selectedController, null, true);
                return true;
            }

            return false;
        }


        void CalculateDeltaTime () {
            m_deltaTime = (EditorApplication.timeSinceStartup - m_lastOnGUICall);
            m_lastOnGUICall = EditorApplication.timeSinceStartup;
        }


        int CreateNodeParameter () {
            return Serializer.CreateNodeParameterToController (controller);
        }


        void RemoveNodeParameter (NodeParameter parameterToRemove) {
            Serializer.RemoveNodeParameterFromController (controller, parameterToRemove);
        }


        NodeParameter[] GetNodeParameters () {
            if (controller == null) {
                return new NodeParameter[0];
            }

            return controller.nodeParameters;
        }


        void ChangeNodeParameterName (NodeParameter parameter, string newName) {
            controller.ChangeNodeParameterName (newName, parameter);
            EditorGUIUtility.RepaintEditorsOfType<AIBehaviourInspector> ();
        }


        void ChangeNodeParameterIndex (NodeParameter parameter, int newIndex) {
            controller.ChangeNodeParameterIndex (newIndex, parameter);
        }


        int CreateExposedParameter () {
            return Serializer.CreateExposedParameterToController (controller);
        }


        void RemoveExposedParameter (ExposedParameter parameterToRemove) {
            Serializer.RemoveExposedParameterFromController (controller, parameterToRemove);
        }


        ExposedParameter[] GetExposedParameters () {
            if (controller == null) {
                return new ExposedParameter[0];
            }

            return controller.exposedParameters;
        }


        void ChangeExposedParameterName (ExposedParameter parameter, string newName) {
            controller.ChangeExposedParameterName (newName, parameter);
            EditorGUIUtility.RepaintEditorsOfType<AIBehaviourInspector> ();
        }


        void ChangeExposedParameterIndex (ExposedParameter parameter, int newIndex) {
            controller.ChangeExposedParameterIndex (newIndex, parameter);
        }


        bool IsGraphMode (GraphMode mode, params GraphMode[] modes) {
            if (m_graphMode == mode) {
                return true;
            }

            for (int i = 0; i < modes.Length; i++) {
                if (modes[i] == m_graphMode) {
                    return true;
                }
            }

            return false;
        }

#endregion


#region Unity Callbacks 

        void Awake () { }


        void OnEnable () {
            #if GRAPH_DEBUG
            Debug.Log ("[AI Graph]: On Enabled");
            Debug.Log ("On Enabled curr controller:" + controller);
            Debug.Log ("On Enabled last controller:" + controllerBeforeEditModeExited);
            Debug.Log ("On Enabled cached controller:" + controllerBeforeGraphClosed);
            Debug.Log ("On Enabled curr ai:" + aiBehaviour);
            Debug.Log ("On Enabled last ai:" + aiBehaviourBeforeEditModeExited);
            Debug.Log ("On Enabled cached ai:" + aiBehaviourBeforeGraphClosed);
            #endif

            InitializeGraph ();

            if (controller != null) {
                LoadController (controller, aiBehaviour, true);
            }
            else if (controllerBeforeGraphClosed != null) {
                LoadController (controllerBeforeGraphClosed, aiBehaviourBeforeGraphClosed, true);
            }
            else {
                LoadController (null, null, true);
            }
        }


        /// <summary>
        /// Load the current selected Controller
        /// </summary>
        void LoadSelectedController () {
            TryLoadSelectedController ();
        }


        void EditorApplicationOnPlayModeStateChanged (PlayModeStateChange state) {
            switch (state) {
                case PlayModeStateChange.EnteredEditMode:
                    #if GRAPH_DEBUG
                    Debug.Log ("[AI Graph]: Enter edit mode");
                    #endif

                    // If the graph enters edit mode.
                    // Try to load the current selected controller
                    // Load the controller before the edit mode was exited.
                    // Set the last toggle index if it was node parameter or exposed parameter,
                    // and the current isn't for the history.
                    if (!TryLoadSelectedController ()) {
                        LoadController (controllerBeforeEditModeExited, aiBehaviourBeforeEditModeExited, true);
                    }

                    if (m_leftPanelSelectedToggleIndexBeforeEditModeExited == 0 || m_leftPanelSelectedToggleIndexBeforeEditModeExited == 1 && m_leftPanelSelectedToggleIndex != 2) {
                        m_leftPanelSelectedToggleIndex = m_leftPanelSelectedToggleIndexBeforeEditModeExited;
                    }

                    break;

                case PlayModeStateChange.ExitingEditMode:
                    #if GRAPH_DEBUG
                    Debug.Log ("[AI Graph]: Exit edit mode: Save current controller");
                    #endif

                    // If the graph wants to exiting the edit mode,
                    // save the current controller and left panel toggle index.
                    controllerBeforeEditModeExited = controller;
                    aiBehaviourBeforeEditModeExited = aiBehaviour;
                    m_leftPanelSelectedToggleIndexBeforeEditModeExited = m_leftPanelSelectedToggleIndex;

                    // We need to clear the undo list because of unity's serialization,
                    // we are not able to serialize the anonymous action delegates.
                    GraphUndo.Clear ();

                    // Save and cleanup all modified controllers.
                    // Same problem of serialization. We cannot go back to a further point.
                    Serializer.CleanupAll ();

                    break;

                case PlayModeStateChange.EnteredPlayMode:
                    #if GRAPH_DEBUG
                    Debug.Log ("[AI Graph]: Enter play mode");
                    #endif

                    // If the graph entered play mode,
                    // try to load the current selected controller.
                    // If no controller is selected check if the loaded
                    // ai has a runtimeController and load that.
                    // Else load the loaded controller or null if no one is loaded.
                    if (!TryLoadSelectedController ()) {
                        if (aiBehaviour != null) {
                            if (aiBehaviour.runtimeController != null) {
                                LoadController (aiBehaviour.runtimeController, aiBehaviour, true);
                            }
                        }
                        else {
                            LoadController (controller, null, true);
                        }
                    }

                    break;

                case PlayModeStateChange.ExitingPlayMode:
                    #if GRAPH_DEBUG
                    Debug.Log ("[AI Graph]: Exit play mode");
                    #endif

                    // If we modified runtimeController,
                    // clear the undo-list because they are not persistent and we cannot go back.
                    GraphUndo.Clear ();
                    break;
            }
        }


        bool UnityWantsToQuit () {
            bool isControllerDirty = false;
            foreach (AIController controllerInProject in Serializer.AllControllerInProject ()) {
                if (controllerInProject.isDirty) {
                    isControllerDirty = true;
                    break;
                }
            }

            if (isControllerDirty) {
                bool close = EditorUtility.DisplayDialog (dialogTitle, dialogMessage, "OK", "Cancel");
                if (close) {
                    return Serializer.CleanupAll (true);
                }

                return false;
            }

            return true;
        }


        void OnDisable () {
            m_canvas.OnDisable ();
        }


        void OnDestroy () { }


        void OnFocus () { }


        void OnLostFocus () { }


        void Update () { }


        void OnGUI () {
            DrawGraph ();

            CalculateDeltaTime ();

            Repaint ();
        }


        void OnSceneGUI (SceneView sceneView) {
            m_canvas.OnSceneGUI ();
        }

#endregion


#region GUI

        void DrawGraph () {
            OnTopToolbarGUI ();

            OnLeftToolbarGUI ();

            OnBottomToolbarGUI ();

            OnLeftPanelGUI ();

            OnCanvasGUI ();

            OnStatisticsGUI ();

            OnCopyControllerWarningGUI ();
        }


        void OnTopToolbarGUI () {
            Rect toolbarRect = new Rect (0, 0, position.width, Styles.topToolbarHeight);
            Rect sliderRect = new Rect (position.width - 210, Styles.topToolbarHeight / 2 - Styles.zoomSlider.fixedHeight / 2, 200, Styles.zoomSlider.fixedHeight);
            Rect snapRect = new Rect (sliderRect.x - 50 - 10, toolbarRect.y, 50, toolbarRect.height);
            m_statsToggleRect = new Rect (snapRect.x - 50, toolbarRect.y, 50, toolbarRect.height);
            Rect drawSimpleRect = new Rect (m_statsToggleRect.x - 60, toolbarRect.y, 60, toolbarRect.height);

            EditorGUI.Toolbar (toolbarRect, false, false, true, true);

            m_canvas.drawSimple = EditorGUI.Toggle (drawSimpleRect, m_canvas.drawSimple, "Simple");
            m_drawStats = EditorGUI.Toggle (m_statsToggleRect, m_drawStats, "Stats");
            m_canvas.snapNodesOnGrid = EditorGUI.Toggle (snapRect, m_canvas.snapNodesOnGrid, "Snap");

            float newZoom = GUI.HorizontalSlider (sliderRect, m_canvas.zoom, m_canvas.minZoom, m_canvas.maxZoom, Styles.zoomSlider, Styles.zoomSliderThumb);
            m_canvas.SetZoom (newZoom, m_canvas.ScreenToCanvasPoint (m_canvasRect.center));
        }


        void OnBottomToolbarGUI () {
            Rect toolbarRect = new Rect (0, position.height - Styles.bottomToolbarHeight, position.width, Styles.bottomToolbarHeight);

            EditorGUI.Toolbar (toolbarRect, false, false, true, true);

            string label = "Loaded";
            float width = EditorGUI.textStyleLeft.CalculateTextWidth (label);

            Rect controllerRect = new Rect (toolbarRect.xMax - 10 - 113, toolbarRect.y + toolbarRect.height / 2 - 7.5F, 113, 15);
            Rect aiBehaviourRect = new Rect (controllerRect.x - 5 - 113, toolbarRect.y + toolbarRect.height / 2 - 7.5F, 113, 15);
            Rect selectRect = new Rect (aiBehaviourRect.x - 5 - width, toolbarRect.y, width, toolbarRect.height);

            EditorGUI.Label (selectRect, label, EditorGUI.textStyleLeft);
            EditorGUI.PingObject (aiBehaviourRect, aiBehaviour, typeof (AIBehaviour));
            EditorGUI.PingObject (controllerRect, controller, typeof (AIController));
        }


        void OnLeftToolbarGUI () {
            Rect backgroundRect = new Rect (0, Styles.topToolbarHeight, Styles.leftToolbarWidth, position.height - Styles.topToolbarHeight - Styles.bottomToolbarHeight);
            EditorGUI.DrawSolidTexture (backgroundRect, EditorGUI.toolbarColor.color);
            EditorGUI.DrawSolidTexture (backgroundRect, EditorGUI.toolbarOutlineColor.color, new BorderWidth (1, 1, 0, 0));

            Rect rect = new Rect (0, position.height - Styles.bottomToolbarHeight, position.height - Styles.topToolbarHeight - Styles.bottomToolbarHeight, Styles.leftToolbarWidth);

            GUIUtility.RotateAroundPivot (-90, rect.min);

            GUILayout.BeginArea (rect);

            EditorGUI.BeginToggleGroup (m_leftPanelSelectedToggleIndex);
            EditorGUI.ToggleForGroup (new Rect (5, 0, 130, rect.height), "Exposed Parameter");
            EditorGUI.ToggleForGroup (new Rect (135, 0, 110, rect.height), "Node Parameter");
            EditorGUI.ToggleForGroup (new Rect (245, 0, 55, rect.height), "History");
            EditorGUI.ToggleForGroup (new Rect (300, 0, 35, rect.height), "Log");
            m_leftPanelSelectedToggleIndex = EditorGUI.EndToggleGroup ();

            GUILayout.EndArea ();

            GUIUtility.RotateAroundPivot (90, rect.min);
        }


        void OnLeftPanelGUI () {
            m_leftPanelAnimationValue += isLeftPanelOpen ? Styles.leftPanelAnimationSpeed * deltaTime : -Styles.leftPanelAnimationSpeed * deltaTime;
            if (m_leftPanelAnimationValue < 0) m_leftPanelAnimationValue = 0;
            if (m_leftPanelAnimationValue > 1) m_leftPanelAnimationValue = 1;

            m_leftPanelRect = new Rect (Styles.leftToolbarWidth, Styles.topToolbarHeight, m_leftPanelRect.width, position.height - Styles.topToolbarHeight - Styles.bottomToolbarHeight);
            m_leftPanelRect.xMax = EditorGUI.HorizontalScaleSlider (new Vector2 (m_leftPanelRect.xMax, m_leftPanelRect.yMin), 10, m_leftPanelRect.height);
            m_leftPanelRect.width = Mathf.Clamp (m_leftPanelRect.width, Styles.leftPanelMinWidth, position.width - Styles.leftToolbarWidth - Styles.leftPanelMinOpenWidth);

            m_visualLeftPanelRect = new Rect (m_leftPanelRect);
            m_visualLeftPanelRect.width = m_leftPanelRect.width * m_leftPanelAnimationValue;

            EditorGUI.DrawSolidTexture (m_visualLeftPanelRect, Styles.leftPanelBackground.color);
            EditorGUI.DrawSolidTexture (m_visualLeftPanelRect, EditorGUI.toolbarOutlineColor.color, new BorderWidth (0, 2, 0, 0));

            if (m_leftPanelSelectedToggleIndex == 0 && m_exposedParameterList != null) {
                m_exposedParameterList.OnGUI (m_visualLeftPanelRect, IsGraphMode (GraphMode.AssetController, GraphMode.AssetControllerAndAiBehaviour) && !EditorApplication.isPlaying);
            }
            else if (m_leftPanelSelectedToggleIndex == 1 && m_nodeParameterList != null) {
                m_nodeParameterList.OnGUI (m_visualLeftPanelRect, IsGraphMode (GraphMode.AssetController, GraphMode.AssetControllerAndAiBehaviour) && !EditorApplication.isPlaying);
            }
            else if (m_leftPanelSelectedToggleIndex == 2) {
                m_historyPanel.OnGUI (m_visualLeftPanelRect);
            }
            else if (m_leftPanelSelectedToggleIndex == 3) {
                m_logPanel.OnGUI (m_visualLeftPanelRect);
            }
        }


        void OnCanvasGUI () {
            m_canvasRect = new Rect (m_visualLeftPanelRect.xMax, Styles.topToolbarHeight, position.width - m_visualLeftPanelRect.xMax, position.height - Styles.topToolbarHeight - Styles.bottomToolbarHeight);

            if (Event.current.type == EventType.Repaint) {
                EditorGUI.DrawSolidTexture (m_canvasRect, Styles.canvasBackgroundColor.color);
            }

            m_canvas.OnGUI (m_canvasRect);

            eventSystem.Process ();
        }


        void OnStatisticsGUI () {
            if (m_drawStats) {
                m_statsPanel.OnGUI (new Rect (m_statsToggleRect.x, m_statsToggleRect.yMax + Styles.statsPanelSpace, Styles.statsPanelWidth, Styles.statsPanelHeight));
            }
        }


        void OnCopyControllerWarningGUI () {
            if (IsGraphMode (GraphMode.RuntimeController, GraphMode.RuntimeControllerAndAiBehaviour)) {
                Rect rect = new Rect (m_canvasRect.x + 20, m_canvasRect.yMax - 50 - 20, 220, 50);

                EditorGUI.DrawSolidTexture (rect, Styles.copyControllerInfoBoxColor.color);
                EditorGUI.Label (rect, "Runtime Controller", Styles.copyControllerInfoStyle);
            }
        }

#endregion


        static class Styles {

            public static readonly float statsPanelSpace = 5;
            public static readonly float statsPanelWidth = 315;
            public static readonly float statsPanelHeight = 98;

            public static readonly float leftPanelAnimationSpeed = 4;
            public static readonly float leftPanelMinWidth = 280;
            public static readonly float leftPanelMinOpenWidth = 380;

            public static readonly EditorColor canvasBackgroundColor = new EditorColor (new Color32 (94, 94, 94, 255), new Color32 (40, 40, 40, 255));
            public static readonly EditorColor copyControllerInfoBoxColor = new EditorColor (new Color32 (0, 0, 0, 60), new Color32 (0, 0, 0, 50));
            public static readonly EditorColor leftPanelBackground = new EditorColor (new Color32 (194, 194, 194, 255), new Color32 (53, 53, 53, 255));

            public static readonly float topToolbarHeight = 21;
            public static readonly float bottomToolbarHeight = 21;
            public static readonly float leftToolbarWidth = 21;

            public static readonly GUIStyle zoomSliderThumb = "preSliderThumb";
            public static readonly GUIStyle zoomSlider = "preSlider";

            public static readonly GUIStyle copyControllerInfoStyle;


            static Styles () {
                copyControllerInfoStyle = new GUIStyle (EditorGUI.textStyleCenter);
                copyControllerInfoStyle.normal.textColor = Color.white;
                copyControllerInfoStyle.fontStyle = FontStyle.Bold;
                copyControllerInfoStyle.fontSize = 18;
            }

        }

    }


}
#endif